/* libfake437 - a library for the rendering of code page 437 `graphics'
 * Copyright (C) 2008, 2009, 2010, 2011 Jack Kelly <endgame.dos@gmail.com>
 *
 * This library is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation, either version 3 of
 * the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "config.h"
#include <assert.h>
#include <SDL.h>
#include "fake437/types.h"
#include "fake437/primitive.h"
#include "fonts.h"

/* As seen in the SDL examples, which was released to the public domain */
static void putpixel(SDL_Surface* surface, int x, int y, Uint32 pixel) {
  int bpp = surface->format->BytesPerPixel;
  /* Here p is the address to the pixel we want to set */
  Uint8* p = (Uint8*)surface->pixels + y * surface->pitch + x * bpp;
  if (x < 0 || x >= surface->w || y < 0 || y >= surface->h) return;
  switch(bpp) {
  case 1:
    *p = pixel;
    break;
  case 2:
    *(Uint16*)p = pixel;
    break;
  case 3:
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
    p[0] = (pixel >> 16) & 0xff;
    p[1] = (pixel >> 8) & 0xff;
    p[2] = pixel & 0xff;
#else
    p[0] = pixel & 0xff;
    p[1] = (pixel >> 8) & 0xff;
    p[2] = (pixel >> 16) & 0xff;
#endif
    break;
  case 4:
    *(Uint32*)p = pixel;
    break;
  }
}

static const SDL_Color default_colors[] = {{  0,   0,   0, 0},
                                           {  0,   0, 168, 0},
                                           {  0, 168,   0, 0},
                                           {  0, 168, 168, 0},
                                           {168,   0,   0, 0},
                                           {168,   0, 168, 0},
                                           {168,  84,   0, 0},
                                           {168, 168, 168, 0},
                                           { 84,  84,  84, 0},
                                           { 84,  84, 252, 0},
                                           { 84, 252,  84, 0},
                                           { 84, 252, 252, 0},
                                           {252,  84,  84, 0},
                                           {252,  84, 252, 0},
                                           {252, 252,  84, 0},
                                           {252, 252, 252, 0}};

const SDL_Color* f437_get_color(F437Color color) {
  assert(color < F437_NUM_COLORS);
  return color == F437_COLOR_TRANSPARENT ? NULL : &default_colors[color];
}

const F437Font* f437_get_font(F437FontName name) {
  switch (name) {
  case F437_FONT_8x8:      return &font_8x8;
  case F437_FONT_8x8_THIN: return &font_8x8_thin;
  case F437_FONT_8x14:     return &font_8x14;
  }
  return NULL;
}

void f437_putc(SDL_Surface* surface, const F437Font* font,
               int x, int y, int scale, int ch,
               const SDL_Color* fg, const SDL_Color* bg) {
  const Uint8* ch_real =
    font->chars + (font->char_width * font->char_height * ch);
  Uint8 ch_pixel;
  int x_pixel;
  int y_pixel;
  int x_scale;
  int y_scale;
  Uint32 bg_color;
  Uint32 fg_color;
  bg_color = bg == NULL ? 0 : SDL_MapRGB(surface->format,
                                         bg->r, bg->g, bg->b);
  fg_color = fg == NULL ? 0 : SDL_MapRGB(surface->format,
                                         fg->r, fg->g, fg->b);
  if (SDL_MUSTLOCK(surface)) SDL_LockSurface(surface);
  for (y_pixel = 0; y_pixel < font->char_height; y_pixel++) {
    for (y_scale = 0; y_scale < scale; y_scale++) {
      for (x_pixel = 0; x_pixel < font->char_width; x_pixel++) {
        for (x_scale = 0; x_scale < scale; x_scale++) {
          ch_pixel = ch_real[x_pixel + y_pixel * font->char_width];
          if ((ch_pixel > 0 && fg == NULL) ||
              (ch_pixel == 0 && bg == NULL)) continue;
          putpixel(surface,
                   (x_pixel * scale) + x + x_scale,
                   (y_pixel * scale) + y + y_scale,
                   ch_pixel > 0 ? fg_color : bg_color);
        }
      }
    }
  }
  if (SDL_MUSTLOCK(surface)) SDL_UnlockSurface(surface);
}
